package com.rizzo.back.dao.impl;

import com.rizzo.back.dao.GenericDao;
import com.rizzo.back.dao.SearchableDao;
import org.apache.lucene.search.Query;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import java.io.Serializable;
import java.util.List;


/**
 * This class serves as the Base class for all other DAOs - namely to hold
 * common CRUD methods that they might all use. You should only need to extend
 * this class when your require custom CRUD logic.
 * <p/>
 * <p/>
 * To register this class in your Spring context file, use the following XML.
 * <p/>
 *
 * @author Jan lievens
 * @param <T>  The Class type of the objects that need to be persisted.
 * @param <PK> The Primary Key that will be used to persist the objects.
 */
public class GenericDaoHibernateImpl<T, PK extends Serializable> extends
        HibernateDaoSupport implements GenericDao<T, PK>, SearchableDao<T, PK> {

    /**
     * The class to be presisted.
     */
    private Class<T> persistentClass;

    /**
     * The constructor needs the <tt>Class</tt> to be persisted. You could
     * provide this property through the Spring application context.
     *
     * @param persistentClass The <tt>Class</tt> to be persisted.
     */
    public GenericDaoHibernateImpl(Class<T> persistentClass) {
        this.persistentClass = persistentClass;
    }

    /**
     * @see GenericDao#getAll()
     */
    public List<T> getAll() {
        return super.getHibernateTemplate().loadAll(this.persistentClass);
    }

    /**
     * @see GenericDao#getAll()
     */
    public T get(PK id) throws ObjectRetrievalFailureException {
        T entity = (T) super.getHibernateTemplate().get(this.persistentClass, id);
        assertEntityRetrieved(entity, id);
        return entity;
    }

    /**
     * @see GenericDao#getAll()
     */
    public boolean exists(PK id) {
        T entity = (T) super.getHibernateTemplate().load(this.persistentClass, id);
        if (entity == null) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * @see GenericDao#getAll()
     */
    public void saveOrUpdate(T object) {
        getHibernateTemplate().saveOrUpdate(object);
        getSession().flush();
    }

    /**
     * @see GenericDao#getAll()
     */
    public void delete(T entity) {
        super.getHibernateTemplate().delete(entity);
    }

    /**
     * @see GenericDao#load(java.io.Serializable) load()
     */
    public T load(PK id) throws ObjectRetrievalFailureException {
        T entity = (T) getHibernateTemplate().load(this.persistentClass, id);
        assertEntityRetrieved(entity, id);
        return entity;
    }

    /**
     * {@inheritDoc}
     */
    public FullTextSession getSearchSession() {
        return Search.getFullTextSession(getSession());
    }

    /**
     * {@inheritDoc}
     */
    public List<T> searchEntity(Query luceneQuery) {
        FullTextQuery fullTextQuery = getSearchSession().createFullTextQuery(luceneQuery, persistentClass);
        return fullTextQuery.list();
    }

    /**
     * {@inheritDoc}
     */
    public void indexAllEntities() {
        List<T> objects = getAll();
        FullTextSession textSession = getSearchSession();
        for (T object : objects) {
            textSession.index(object);
        }
        textSession.flushToIndexes();
    }

    /**
     * Method assertEntityRetrieved asserts if the Entity was retrieved.
     *
     * @param entity of type T
     * @param id     of type PK
     * @throws ObjectRetrievalFailureException
     *          when
     */
    private void assertEntityRetrieved(T entity, PK id) throws ObjectRetrievalFailureException {
        if (entity == null) {
            throw new ObjectRetrievalFailureException(this.persistentClass, id);
        }
    }

}
